In [1]:
import numpy as np
import pandas as pd
import pandas
from matplotlib import pyplot as plt
import seaborn as sn
%matplotlib inline
In [2]:
art_data = pandas.read_csv("./TestDataSource/art.csv", sep = ';')
sales_data = pandas.read_csv("./TestDataSource/sales.csv", sep = ';')
store_data = pandas.read_csv("./TestDataSource/store.csv", sep = ';')
txt_data = pandas.read_csv("./TestDataSource/txn.csv", sep = ';')
In [10]:
sales_data = sales_data.replace({',': '.'}, regex=True)
txt_data = txt_data.replace({',': '.'}, regex=True)
In [11]:
sales_data[[ 'SALES']] = sales_data[[ 'SALES']].apply(pd.to_numeric)
txt_data[['SALES']] = txt_data[[ 'SALES']].apply(pd.to_numeric)
In [20]:
txt_data.DAY.unique()
Out[20]:
In [24]:
mean_check = pd.merge(
txt_data[txt_data.DAY < '2015-11-01'].groupby(['TXN', 'STORE'], as_index=False)['SALES'].sum().groupby('STORE').mean(),
txt_data[txt_data.DAY >= '2015-11-01'].groupby(['TXN', 'STORE'], as_index=False)['SALES'].sum().groupby('STORE').mean(),
left_index=True, right_index=True)
mean_check.drop(['TXN_x','TXN_y'], axis =1, inplace = True)
In [26]:
mean_check.columns = ['Окт', 'Нояб']
In [27]:
mean_check.plot.bar(title = 'Средний чек магазина', figsize = (15,5))
Out[27]:
Средний чек в ноябре больше чем в октябре
In [29]:
mean_check = pd.merge(
txt_data[txt_data.DAY < '2015-11-01'].groupby(['TXN', 'STORE', 'DAY'], as_index=False)['SALES'].count().groupby(['STORE', 'DAY'], as_index=False).count().groupby('STORE').mean(),
txt_data[txt_data.DAY >= '2015-11-01'].groupby(['TXN', 'STORE', 'DAY'], as_index=False)['SALES'].count().groupby(['STORE', 'DAY'], as_index=False).count().groupby('STORE').mean(),
left_index=True, right_index=True)
In [30]:
mean_check.drop(['TXN_x','TXN_y'], axis =1, inplace = True)
mean_check.columns = ['Окт', 'Нояб']
mean_check.plot.bar(title = 'Ср. колво чеков магазина в 1 день', figsize = (15,5))
Out[30]:
In [ ]:
Ср. колво чеков при этом оказывается в октябре больше
In [31]:
# ср. продажи на 1 магазин. в день
mean_check = pd.merge(
txt_data[txt_data.DAY < '2015-11-01'].groupby(['TXN', 'STORE', 'DAY'], as_index=False)['SALES'].sum().groupby(['STORE', 'DAY'], as_index=False).sum().groupby('STORE').mean(),
txt_data[txt_data.DAY >= '2015-11-01'].groupby(['TXN', 'STORE', 'DAY'], as_index=False)['SALES'].sum().groupby(['STORE', 'DAY'], as_index=False).sum().groupby('STORE').mean(),
left_index=True, right_index=True)
mean_check.drop(['TXN_x','TXN_y'], axis =1, inplace = True)
mean_check.columns = ['Окт', 'Нояб']
mean_check.plot.bar(title = 'Ср. продажи на 1 магазин. в день', figsize = (15,5))
Out[31]:
Средние продажи по магазинам однозначной разницы в сумме продаж от месяца к месяцу нет, нужен дополнительный анализ
In [346]:
topitems = pd.merge(sales_data, art_data, how = 'inner')
In [347]:
topitems = topitems.groupby(['ART','NAME', 'GRP'], as_index = False).sum()
topitems.head()
topitems.drop(['STORE'], axis =1, inplace = True)
In [348]:
topitems.sort_values(by = ['GRP','SALES'], ascending = [True, False], inplace = True)
In [349]:
topitems.head()
Out[349]:
In [43]:
pd.options.display.float_format = '{:,.2f}'.format
In [351]:
cum_sums = topitems.groupby(['GRP'], as_index = False).cumsum()
cum_sums.drop('ART', axis = 1, inplace = True)
cum_sums.columns = ['CumSum']
In [352]:
cum_sums = pd.merge(cum_sums, topitems, left_index=True, right_index=True)
In [353]:
cum_sums['GRP_sum'] = cum_sums.groupby('GRP').SALES.transform(np.sum)
In [354]:
cum_sums['prop_sum'] = 1.*cum_sums.CumSum/cum_sums.GRP_sum
In [356]:
a_min_group = cum_sums[cum_sums.prop_sum >= 0.5][['GRP','prop_sum' ]].groupby('GRP', as_index = False).min()
a_min_group.columns = ['GRP', 'min_prop50']
cum_sums = pd.merge(cum_sums, a_min_group)
In [357]:
a_min_group = cum_sums[cum_sums.prop_sum >= 0.8][['GRP','prop_sum' ]].groupby('GRP', as_index = False).min()
a_min_group.columns = ['GRP', 'min_prop80']
cum_sums = pd.merge(cum_sums, a_min_group)
In [364]:
cum_sums[cum_sums.prop_sum <= cum_sums.min_prop50][['GRP', 'NAME', 'SALES']]
Out[364]:
In [365]:
cum_sums[cum_sums.prop_sum <= cum_sums.min_prop80][['GRP', 'NAME', 'SALES']]
Out[365]:
In [3]:
txt_data = pandas.read_csv("./TestDataSource/txn.csv", sep = ';')
txt_data = txt_data.replace({',': '.'}, regex=True)
txt_data[['SALES']] = txt_data[[ 'SALES']].apply(pd.to_numeric)
In [9]:
txt_data = txt_data.groupby(['STORE', 'DAY'], as_index=False)['SALES'].sum()
txt_data.sort_values(by = ['STORE','DAY'], inplace = True)
In [5]:
txt_data.STORE.unique()
Out[5]:
In [30]:
for store in txt_data.STORE.unique():
plt.plot(txt_data[txt_data.STORE == store].DAY, txt_data[txt_data.STORE == store].SALES, linewidth=0.5)
t = [txt_data['DAY'].min(), txt_data['DAY'].max()]
plt.xticks(t,t)
Out[30]:
Есть явная понедельная сезонность. Отдельно можно строить логреги по исходным данным + доп признакам, но остановимся на простом baseline'e - значение 6 дней назад от последнего
Восстановить данные за 2015-11-30
In [46]:
print('STORE 2015-11-30 SALES\n')
for store in txt_data.STORE.unique():
print( store, txt_data[(txt_data.STORE == store) & (txt_data.DAY == '2015-11-24')].SALES.sum())
Средний чек магазина, ср. кол-во чеков на 1 магазин. в день, ср. продажи на 1 магазин. в день
In [48]:
txt_data = pandas.read_csv("./TestDataSource/txn.csv", sep = ';')
txt_data = txt_data.replace({',': '.'}, regex=True)
txt_data[['SALES']] = txt_data[[ 'SALES']].apply(pd.to_numeric)
In [50]:
mean_check = txt_data.groupby(['TXN', 'STORE', 'DAY'], as_index=False)['SALES'].count().groupby(['STORE', 'DAY'], as_index=False).count()
mean_check.sort_values(by = ['STORE','DAY'], inplace = True)
mean_check.drop('SALES', axis = 1, inplace = True)
In [52]:
for store in mean_check.STORE.unique():
plt.plot(mean_check[mean_check.STORE == store].DAY, mean_check[mean_check.STORE == store].TXN, linewidth=0.5)
t = [mean_check['DAY'].min(), mean_check['DAY'].max()]
plt.xticks(t,t)
Out[52]:
In [53]:
print('STORE 2015-11-30 TXN\n')
for store in mean_check.STORE.unique():
print( store, mean_check[(mean_check.STORE == store) & (mean_check.DAY == '2015-11-24')].TXN.sum())
In [58]:
mean_check = txt_data.groupby(['TXN', 'STORE', 'DAY'], as_index=False)['SALES'].sum().groupby(['STORE', 'DAY'], as_index=False).mean()
mean_check.sort_values(by = ['STORE','DAY'], inplace = True)
mean_check.drop('TXN', axis = 1, inplace = True)
print('STORE 2015-11-30 SALES\n')
for store in mean_check.STORE.unique():
print( store, mean_check[(mean_check.STORE == store) & (mean_check.DAY == '2015-11-24')].SALES.sum())
PS Можно отдельно поработать над репрезентацией данных
Так или иначе предоставленное решение является базовым.
In [23]:
txt_data = txt_data.replace({',': '.'}, regex=True)
txt_data[['SALES']] = txt_data[[ 'SALES']].apply(pd.to_numeric)
tran_group = txt_data.groupby(['TXN', 'STORE', 'DAY'], as_index=False)['SALES'].sum().groupby(['STORE', 'DAY'], as_index=False).sum()
tran_group['DAY'] = pd.to_datetime(tran_group['DAY'])
In [24]:
tran_group['weekday'] = tran_group.DAY.dt.dayofweek
In [27]:
# Draw a nested violinplot and split the violins for easier comparison
sn.violinplot(x="weekday", y="SALES" , data=tran_group) #, palette={"Male": "b", "Female": "y"}
sn.despine(left=True)
Визуальный анализ показывает, что для объема продаж значимости дня недели нет. Таким образом, решение в п.3 не совсем корректно. Тем неменее оставим его в текущем виде.
Посмотрим на доверительные интервалы (95% двухсторонняя альтернатива):
In [45]:
import math
from statsmodels.stats.weightstats import _zconfint_generic, _tconfint_generic
In [44]:
for weekday in range(0,7):
print( weekday, _tconfint_generic(tran_group[tran_group.weekday == weekday].SALES.mean(), tran_group[tran_group.weekday == weekday].SALES.std(ddof=1)/math.sqrt(len(tran_group[tran_group.weekday == weekday].SALES)),
len(tran_group[tran_group.weekday == weekday].SALES) - 1, 0.05, 'two-sided'))
Построеннные доверительные интервалы подтверждают гипотезу о независимости продаж и .
В рамках дальнейшего анализа можно посмотреть завимость дня недели по разным товарам/групп товаров и разрезе определенных магазинов
In [ ]: